package com.timk.goserver.client.sgf;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.timk.goserver.client.compatibility.Point;

/**
 * Represents a node within an SGF file.
 * @author TKington
 *
 */
public class SGFNode {
	/** The parent node. */
    protected SGFNode parent;
    /** This node's children. */
    protected List children;
    
    /** Node name */
    private String name;
    /** Node comment. */
    protected String comment;
	/** This object represents a move at the point pos. */
	private Point pos;
	/** Player who plays this move. */
	protected int player;
	
	private final Map properties = new HashMap();
    
	/**
	 * Creates an SGFNode.
	 * @param parent the parent node
	 */
    private SGFNode(SGFNode parent) {
        this.parent = parent;
        children = new ArrayList();
    }
    
    /**
     * Creates an SGFNode representing a move on the Board.
     * @param parent the parent SGFNode
     * @param x the x location
     * @param y the y location
     */
    public SGFNode(SGFNode parent, int x, int y) {
        this(parent);
        pos = new Point(x,y);
        player = parent.player * -1;
        comment = ""; //$NON-NLS-1$
    }
    
    /**
     * Creates an SGFNode.
     * @param sgfData an SGF representation of the node
     * @param parent the parent SGFNode
     * @throws SGFParseException if the String can't be parsed successfully
     */
    public SGFNode(String sgfData, SGFNode parent) throws SGFParseException {
        this(parent);
        
        parse(sgfData);
    }
    
    /**
     * Populates this SGFNode with the data in the passed SGF string.
     * @param sgfData the SGF description of the node
     * @throws SGFParseException if the SGF string is illegal
     */
    protected final void parse(String sgfData) throws SGFParseException {
    	comment = null;
    	
		try {
            SGFParser.parseTags(sgfData, this);
        }
        catch(Exception e) {
            throw new SGFParseException(e.getMessage() + " sgf=" + sgfData, e); //$NON-NLS-1$
        }
	}

    /**
	 * Adds an SGF tag and properties to the current node.
	 * @param tag the SGF tag
	 * @param propVals the properties
	 * @throws SGFParseException if the properties are illegal
	 */
    public void addTag(String tag, List propVals) throws SGFParseException {
        if("C".equals(tag)) { //$NON-NLS-1$
            comment = (String)propVals.get(0);
        }
        else if("B".equals(tag)) { //$NON-NLS-1$
            player = 1;
            pos = SGFUtils.stringToPoint((String)propVals.get(0));
        }
        else if("W".equals(tag)) { //$NON-NLS-1$
            player = -1;
            pos = SGFUtils.stringToPoint((String)propVals.get(0));
        }
        else if("N".equals(tag)) {
        	name = (String)propVals.get(0);
        }
        else {
        	properties.put(tag, propVals.get(0));
        }
    }
    
    /**
     * Adds a child SGFNode.
     * @param node the child
     */
    public void addChild(SGFNode node) {
        children.add(node);
    }

	/**
	 * Returns the SGFNode representing a follow-up at the given Point.
	 * @param point the Point
	 * @return the SGFNode, or null if there is no follow-up at that Point
	 */
	public SGFNode getNextNode(Point point) {
	    for(int i = 0; i < children.size(); i++) {
	        SGFNode node = (SGFNode)children.get(i);
	        if(node.pos != null && node.pos.equals(point)) {
				return node;
			}
	    }
	    return null;
	}
	
	/**
	 * Returns true if this SGFNode has follow-up moves.
	 * @return true if this SGFNode has follow-up moves
	 */
	public boolean hasChildMoves() {
	    for(int i = 0; i < children.size(); i++) {
	        SGFNode node = (SGFNode)children.get(i);
	        if(node.pos != null) {
				return true;
			}
	    }
	    return false;
	}

	/**
	 * Returns the current player at this SGFNode.
	 * @return 1 for black, -1 for white
	 */
	public int getPlayer() { return player; }
	
	/**
	 * Sets the current player at this SGFNode.
	 * @param player 1 for black, -1 for white
	 */
	public void setPlayer(int player) { this.player = player; }
	
	/**
	 * Returns the parent of this SGFNode. 
	 * @return the parent
	 */
    public SGFNode getParent() { return parent; }
    
    /**
     * Returns the first child of this SGFNode.
     * @return the first child, or null if there are no children
     */
    public SGFNode getFirstChild() {
    	if(children.isEmpty()) {
			return null;
		}
    	return (SGFNode)children.get(0);
    }
    
    /**
     * Sets the parent of this SGFNode.
     * @param parent the parent
     */
    public void setParent(SGFNode parent) { this.parent = parent; }
    
    /**
     * Returns the comment at this SGFNode.
     * @return the comment
     */
    public String getComment() { return comment; }
    
    /**
     * Sets the comment at this SGFNode.
     * @param comment the comment
     */
    public void setComment(String comment) { this.comment = comment; }
    
    /**
     * Returns the point where this move is played.
     * @return the point
     */
    public Point getPoint() { return pos; }
    
    /**
     * Returns the node name
     * @return the node name
     */
    public String getName() { return name; }

    /**
     * Returns a Map containing the unhandled properties for this node
     * @return a Map&lt;String, String&gt; containing the unhandled properties 
     * for this node
     */
	public Map getProperties() {
		return properties;
	}
}
